package com.ruan.config;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruan.common.Code;
import com.ruan.common.MessageConstant;
import com.ruan.common.Result;
import com.ruan.mapper.AdminMapper;
import com.ruan.mapper.CommonUserMapper;
import com.ruan.mapper.EmployeeMapper;
import com.ruan.pojo.Admin;
import com.ruan.pojo.CommonUser;
import com.ruan.pojo.Employee;
import com.ruan.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Configuration
@EnableWebSecurity
@Slf4j
@EnableGlobalMethodSecurity( prePostEnabled = true,securedEnabled = true)
@CrossOrigin
public class SecurityConfig {


    @Autowired
    private EmployeeService employeeService;
//    @Resource
//    private AdminAuthorizeService adminAuthorizeService;
//Spring 容器会调用该方法创建一个 SecurityFilterChain 对象，并将其纳入 Spring 容器的管理中
    @Bean//安全过滤器链
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        //使用 http.authorizeRequests() 开始配置请求的授权规则
         return http.authorizeRequests()
//                 .anyRequest().authenticated()
                 .and()
                 //使用 formLogin() 配置登录页面和登录处理逻辑
                 .formLogin()
                 //指定登录表单提交的 URL。
                 .loginProcessingUrl("/user/login/*")
                 //这两个分别配置登录成功和登录失败时的处理逻辑。
                 .successHandler(this::onAuthenticationSuccess)
                 .failureHandler(this::onAuthenticationFailure)
                 //允许所有用户访问登录页面。
                 .permitAll()
                 .and()
                 .csrf()
                 //禁用 CSRF 防护功能。
                 .disable()
                 //cors() 配置跨域资源共享。
                 .cors()
                 .configurationSource(this.corsConfigurationSource())
                 .and()
                 // 配置基本的 HTTP 认证
                 .httpBasic()
                 .and()//拦截所有基于http请求
                 //配置会话管理策略为无状态。
                 .sessionManagement()
                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                 .and()
                 .exceptionHandling()
                 //配置认证入口点为登录失败时的处理逻辑。
                 .authenticationEntryPoint(this::onAuthenticationFailure)
                 .and()
                 //配置认证入口点为登录失败时的处理逻辑。
                 .build();
    }

    private CorsConfigurationSource corsConfigurationSource(){
        //创建一个CorsConfiguration对象，用于配置跨域请求的规则。
        CorsConfiguration configuration = new CorsConfiguration();
        //设置允许的源，这里使用通配符*表示允许所有来源的请求
        configuration.addAllowedOriginPattern("*");
        //设置允许的请求头，同样使用通配符*表示允许所有请求头。
        configuration.addAllowedHeader("*");
        //设置是否允许请求携带凭据（如Cookie、Authorization Header等）
        configuration.setAllowCredentials(true);
        //设置允许的请求方法，使用通配符*表示允许所有请求方法
        configuration.addAllowedMethod("*");
        //设置允许的请求方法，使用通配符*表示允许所有请求方法
        configuration.addExposedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        source.registerCorsConfiguration("/**",configuration);
        return source;
    }

    //创建一个BCryptPasswordEncoder的Bean实例，
    //并将其注册到Spring容器中。
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

//调用其方法来加密用户密码
//@Autowired
//private BCryptPasswordEncoder passwordEncoder;
//
//    public void registerUser(User user){
//        String encodedPassword = passwordEncoder.encode(user.getPassword());
//        user.setPassword(encodedPassword);
//    }


    @Resource
    private AdminMapper adminMapper;

    @Resource
    private CommonUserMapper commonUserMapper;

    @Resource
    private EmployeeMapper employeeMapper;

    @Resource
    private BCryptPasswordEncoder encoder;

    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;Charset=UTF-8");

        String requestURI = request.getRequestURI();
        String username = request.getParameter("username");
        String password = request.getParameter("password");
//        System.out.println("username用户名是："+username);
//        System.out.println("password密码是："+password);

        if (username.equals("admin") && requestURI.equals("/user/login/admin")){
//            System.out.println("管理员登录。。。");
            //1.验证是否已经登录
            String adminInSession = (String) request.getSession().getAttribute("admin");
            if (adminInSession!=null){
                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,"管理员账号已经登录！")));
                return;
            }

            //2.根据页面提交的用户名查询管理员数据库
            LambdaQueryWrapper<Admin> queryWrapper=new LambdaQueryWrapper<>();
            queryWrapper.eq(Admin::getUsername,username);
            Admin adminQuery = adminMapper.selectOne(queryWrapper);

            //3.如果没有查询到则返回登录失败结果
            if (adminQuery == null){
                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,MessageConstant.LOGIN_ERR_PASS)));
                return;
            }

            //4.密码比对，如果不一致就返回登录失败结果
            if (!encoder.matches(password,adminQuery.getPassword())){
                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,MessageConstant.LOGIN_ERR_PASS)));
                return;
            }
            request.getSession().setAttribute("admin",adminQuery.getUsername());
//            System.out.println("管理员的Session存入了？");
            response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_OK,adminQuery, MessageConstant.LOGIN_OK)));
        }else if (!username.equals("admin") && requestURI.equals("/user/login/employee")){
//            System.out.println("普通员工登录。。。");

            //其他新用户登录时验证
            String employeeInSession = (String) request.getSession().getAttribute("employee");
            System.out.println(employeeInSession + "employeeInSession对比username" + username);
            if (employeeInSession != null && employeeInSession.equals(username)) {
                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR, "该普通员工已登录！")));
                return;
            } else {
                // 登录成功就将用户名存入session，以便在需要时获取
                request.getSession().setAttribute("employee", username);
                System.out.println("普通员工的Session存入了？");
            }

//            String employeeInSession = (String) request.getSession().getAttribute("employee");
//            System.out.println(employeeInSession+"employeeInSession对比username"+username);
//            if (employeeInSession!=null && employeeInSession.equals(username)){
//                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,"该普通员工已经登录！")));
//                return;
//            }

//            if (employeeInSession!=null){
//                System.out.println("employeeInSession是什么："+employeeInSession);
//                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,"普通员工账号已经登录！")));
//                return;
//            }

            LambdaQueryWrapper<CommonUser> queryWrapper=new LambdaQueryWrapper<>();
            queryWrapper.eq(CommonUser::getUsername,username);
            CommonUser commonUserQuery = commonUserMapper.selectOne(queryWrapper);

            //3、如果没有查询到则返回登录失败结果
            if (commonUserQuery == null){
                System.out.println("没有该用户。。。。。");
                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,MessageConstant.LOGIN_ERR_PASS)));
                return;
            }
            //4、密码对比，如果不一致返回登录失败结果

            if (!encoder.matches(password,commonUserQuery.getPassword())){
                System.out.println("密码错误。。。。");
                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,MessageConstant.LOGIN_ERR_PASS)));
                return;
            }

            //5、如果账号状态为”禁用“，则返回登录失败
            if (commonUserQuery.getStatus().equals("禁用")){
                response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,MessageConstant.LOGIN_ERR_STATUS)));
                return;
            }
            String phone = commonUserQuery.getPhone();
            LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Employee::getPhone,phone);
            Employee employee = employeeMapper.selectOne(lambdaQueryWrapper);
            System.out.println("Security***************************************************");

            //6、入职登记待审核中，返回登录失败
            if (employee!=null){
                if (employee.getStatus().equals("待审核")){
//                    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                    System.out.println("获取到request.getSession().getAttribute了吗"+
                            request.getSession().getAttribute("employee"));
                    request.getSession().removeAttribute("employee");//尝试登录失败，清空重复登录验证session
                    System.out.println("清除掉request.getSession().getAttribute了吗"+
                            request.getSession().getAttribute("employee"));
                    response.getWriter().write(JSONObject.toJSONString
                            (new Result(Code.LOGIN_ERR,MessageConstant.LOGIN_ERR_AUDIT)));
                    return;
                }
            }
            //登录凭证
            request.getSession().setAttribute("employee",commonUserQuery.getUsername());
            response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_OK,commonUserQuery, MessageConstant.LOGIN_OK)));
        }else {
            response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,MessageConstant.LOGIN_ERR_PASS)));

        }
    }
    //提示用户用户名或密码错误
    //处理用户登录认证失败时
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
        //设置请求的字符编码为 UTF-8，以确保正确处理中文字符。
        request.setCharacterEncoding("utf-8");
        //设置响应的内容类型为 text/html，并指定字符集为 UTF-8。
        response.setContentType("text/html;Charset=UTF-8");
        //将认证失败的结果以 JSON 格式返回给前端。其中，JSONObject.toJSONString(new Result(Code.LOGIN_ERR, exception.getMessage()))
        // 将认证失败的信息封装成一个 Result 对象，并转换成 JSON 字符串返回给前端。
        response.getWriter().write(JSONObject.toJSONString(new Result(Code.LOGIN_ERR,exception.getMessage())));
    }

}
